#include "my_time.h"
#include "my_rwlock.h"

static char  *week[]    = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
static char  *months[]  = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
                            "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };

static my_rwlock_t          rwlock = MY_RWLOCK_INITLIZER;
static int64_t              g_cache_time = 0;
static char                 g_cache_http_time[MY_TIME_HTTP_TIME_SIZE];
static char                 g_cache_log_time[MY_TIME_LOG_TIME_SIZE];

// format: "Mon, 28 Sep 1970 06:00:00 GMT"
int
my_http_time(int64_t time_sec, char *buf, size_t buf_size)
{
    if (buf_size < MY_TIME_HTTP_TIME_SIZE) {
        return 0;
    }

    time_t time = time_sec;

    struct tm gmt;
    gmtime_r(&time, &gmt);
    int n = snprintf(buf, (size_t)buf_size, "%s, %02d %s %4d %02d:%02d:%02d GMT",
        week[gmt.tm_wday], gmt.tm_mday, months[gmt.tm_mon],
        gmt.tm_year + 1900, gmt.tm_hour, gmt.tm_min, gmt.tm_sec);
    
    return n;
}

// format "2021/10/10 10:10:10"
int
my_log_time(int64_t time_sec, char *buf, size_t buf_size)
{
    if (buf_size < MY_TIME_LOG_TIME_SIZE) {
        return 0;
    }

    time_t time = time_sec;

    struct tm ltm;
    localtime_r(&time, &ltm);
    int n = snprintf(buf, (size_t)buf_size, "%04d/%02d/%02d %02d:%02d:%02d",
        ltm.tm_year + 1900, ltm.tm_mon + 1, ltm.tm_mday, ltm.tm_hour, ltm.tm_min, ltm.tm_sec);

    return n;
}

int64_t
get_cache_time()
{
    int64_t    cache_time;

    my_rwlock_rlock(&rwlock);
    cache_time = g_cache_time;
    my_rwlock_runlock(&rwlock);

    if (cache_time != 0) {
        return cache_time;
    }

    return get_latest_time();
}

int64_t
get_latest_time()
{
    my_timespec_t   ts;
    int64_t         time_sec;
    int64_t         rs;
    int64_t         cache_time;

    clock_gettime(CLOCK_REALTIME, &ts);
    rs = my_ts_2_usec(&ts);

    my_rwlock_rlock(&rwlock);
    cache_time = g_cache_time;
    my_rwlock_runlock(&rwlock);

    if (rs - cache_time < 3000) {
        return cache_time;
    }

    my_rwlock_wlock(&rwlock);
    if (rs > g_cache_time) {
        g_cache_time = rs;
        time_sec = my_usec_2_sec(g_cache_time);
        my_http_time(time_sec, g_cache_http_time, MY_TIME_HTTP_TIME_SIZE);
        my_log_time(time_sec, g_cache_log_time, MY_TIME_LOG_TIME_SIZE);
    }
    my_rwlock_wunlock(&rwlock);

    return rs;
}

int
my_cache_http_time(char *buf, size_t buf_size)
{
    if (buf_size < MY_TIME_HTTP_TIME_SIZE) {
        return 0;
    }

    my_bool_t done = MY_FALSE;

    my_rwlock_rlock(&rwlock);
    if (g_cache_time != 0) {
        memcpy(buf, g_cache_http_time, MY_TIME_HTTP_TIME_SIZE);
        done = MY_TRUE;
    }
    my_rwlock_runlock(&rwlock);

    if (done) {
        return MY_TIME_HTTP_TIME_SIZE;
    }

    // update cache time
    get_latest_time();

    my_rwlock_rlock(&rwlock);
    memcpy(buf, g_cache_http_time, MY_TIME_HTTP_TIME_SIZE);
    my_rwlock_runlock(&rwlock);

    return MY_TIME_HTTP_TIME_SIZE;
}

int
my_cache_log_time(char *buf, size_t buf_size)
{
    if (buf_size < MY_TIME_LOG_TIME_SIZE) {
        return 0;
    }

    my_bool_t done = MY_FALSE;

    my_rwlock_rlock(&rwlock);
    if (g_cache_time != 0) {
        memcpy(buf, g_cache_log_time, MY_TIME_LOG_TIME_SIZE);
        done = MY_TRUE;
    }
    my_rwlock_runlock(&rwlock);

    if (done) {
        return MY_TIME_LOG_TIME_SIZE;
    }

    // update cache time
    get_latest_time();

    my_rwlock_rlock(&rwlock);
    memcpy(buf, g_cache_log_time, MY_TIME_LOG_TIME_SIZE);
    my_rwlock_runlock(&rwlock);

    return MY_TIME_LOG_TIME_SIZE;
}
